/**
 * 
 */
package edu.rice.myxo.olexii1;

import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

import edu.rice.myxo.utils.*;

/**
 * @author Haiyang Zhang This class defines myxo cells' internal parameters and
 *         their behaviors.
 */
public class MyxoAgent2 implements Cloneable {

	/**
	 * Initialize a myxo agent(cell). initialType = 1: cells are uniformly
	 * distributed in the rectangle range defined by Global.iniWidth and
	 * Global.iniHeight. Cells direction is either aligned or random.(default)
	 * initialType = 2: cells are uniformly distributed in the rectangle range
	 * defined by Global.iniWidth and Global.iniHeight. Colony swarm expands
	 * initialType = 3: cells are distributed in the rectangle range defined by
	 * Global.iniWidth and Global.iniHeight with a gradient. Colony swarm
	 * expands. initialType = 4: special two opposite cone distribution, half
	 * cells in one cone and half cells in the other.
	 */
	public MyxoAgent2(AgentSpace aSpace, SlimeSpace sSpace,
			ChemicalSpace cSpace, int cellID) {
		super();
		tagAgent();
		vAmoter = Global.vAmoter;
		vSmoter = Global.vSmoter;
		vPhase = Global.vPhase;
		vReproduct = Global.vReproduct;
		phaseReproduct = 2 * Math.PI * Global.r.nextDouble();
		agentSpace = aSpace;
		slimeSpace = sSpace;
		cSignalSpace = cSpace;
		/*
		 * 1. equal distribute in ini_rectangle area (same as whole space); 2.
		 * equal distribute in ini_rectangle area; 3. gradient distribute in
		 * ini_rectangle area; 4. Special distribution, two cone; default: equal
		 * distribution in ini_rectangle area (same as whole space).
		 */
		initialType = (int) Global.initialType;
		switch (initialType) {
		case 1:
			x = Global.iniWidth * Global.r.nextDouble();
			y = Global.iniHeight * Global.r.nextDouble();
			phase = 2 * Math.PI * Global.r.nextDouble();
			if (IFALIGNED) {
				dir = phase < Math.PI ? 0.0000002 * (Global.r.nextDouble() - 0.5)
						: Math.PI + 0.0000002 * (Global.r.nextDouble() - 0.5);
			} else {
				dir = 2 * Math.PI * Global.r.nextDouble();
			}
			break;
		case 2:
			x = Global.iniWidth * Global.r.nextDouble();
			y = Global.iniHeight * Global.r.nextDouble();
			phase = 2 * Math.PI * Global.r.nextDouble();
			if (IFALIGNED) {
				dir = phase < Math.PI ? 0.0000002 * (Global.r.nextDouble() - 0.5)
						: Math.PI + 0.0000002 * (Global.r.nextDouble() - 0.5);
			} else {
				dir = 2 * Math.PI * Global.r.nextDouble();
			}
			break;
		case 3:
			double p = 0;
			double a = 1;
			while (a > p) {
				x = Global.iniWidth * Global.r.nextDouble();
				p = 1 / (Math.pow(x / Global.iniWidth + 0.5, 15) + 1);
				a = Global.r.nextDouble();
			}
			y = Global.iniHeight * Global.r.nextDouble();
			phase = 2 * Math.PI * Global.r.nextDouble();
			if (IFALIGNED) {
				dir = phase < Math.PI ? 0.0000002 * (Global.r.nextDouble() - 0.5)
						: Math.PI + 0.0000002 * (Global.r.nextDouble() - 0.5);
			} else {
				dir = 2 * Math.PI * Global.r.nextDouble();
			}
			break;
		case 4:
			if (cellID < Global.cellNum / 2) {
				// Cone angle must be change from degrees to radians when
				// reading the parameter
				dir = Global.coneAngle * (Global.r.nextDouble() - 0.5);
				double initialRadius = Global.iniRadius * Global.r.nextDouble();
				x = initialRadius * Math.cos(dir) + Global.originX;
				y = initialRadius * Math.sin(dir) + Global.originY;
				if (Global.r.nextBoolean()) {
					dir += Math.PI;
				}
				if (dir < 0) {
					dir += 2 * Math.PI;
				}
				phase = 2 * Math.PI * Global.r.nextDouble();
			} else {
				double farOriX = Global.originX + 2 * Global.iniRadius
						* Math.cos(Global.coneAngle / 2);
				double farOriY = Global.originY;
				dir = Global.coneAngle * (Global.r.nextDouble() - 0.5)
						+ Math.PI;
				double initialRadius = Global.iniRadius * Global.r.nextDouble();
				x = farOriX + initialRadius * Math.cos(dir);
				y = initialRadius * Math.sin(dir) + farOriY;
				if (Global.r.nextBoolean()) {
					dir += Math.PI;
				}
				if (dir > 2 * Math.PI) {
					dir %= 2 * Math.PI;
				}
				phase = 2 * Math.PI * Global.r.nextDouble();
			}
			break;
		default:
			x = Global.iniWidth * Global.r.nextDouble();
			y = Global.iniHeight * Global.r.nextDouble();
			phase = 2 * Math.PI * Global.r.nextDouble();
			if (IFALIGNED) {
				dir = phase < Math.PI ? 0.0000002 * (Global.r.nextDouble() - 0.5)
						: Math.PI + 0.0000002 * (Global.r.nextDouble() - 0.5);
			} else {
				dir = 2 * Math.PI * Global.r.nextDouble();
			}
			break;
		}
		setID(cellID);
		agentSpace = aSpace;
		cellLength = Global.cellLength;
		cellWidth = Global.cellWidth;
		theta = dir > Math.PI ? dir - Math.PI : dir;
		headX = x + cellLength * Math.cos(dir) / 2.0;
		headY = y + cellLength * Math.sin(dir) / 2.0;
		tailX = x - cellLength * Math.cos(dir) / 2.0;
		tailY = y - cellLength * Math.sin(dir) / 2.0;
		dirOld = dir;
		gridX = (int) (x / agentSpace.getGridSize());
		gridY = (int) (y / agentSpace.getGridSize());
		headGridX = (int) (headX / agentSpace.getGridSize());
		headGridY = (int) (headY / agentSpace.getGridSize());
		phaseOld = phase;
		phaseReproduct = 2 * Math.PI * Global.r.nextDouble();
		tailGridX = (int) (tailX / agentSpace.getGridSize());
		tailGridY = (int) (tailY / agentSpace.getGridSize());
		thetaOld = theta;
		xOld = x;
		yOld = y;
		vMoterMean = Math.log(Global.vAmoter)
				- 0.5
				* Math.log(1 + Math.pow(
						(Global.velocityDiffCoef / Global.vAmoter), 2));
		vMoterStd = Math.sqrt(Math.log(1 + Math.pow(
				(Global.velocityDiffCoef / Global.vAmoter), 2)));
		vPhaseMean = Math.log(Global.vPhase)
				- 0.5
				* Math.log(1 + Math.pow(
						(Global.vPhaseDiffCoef / Global.vPhase), 2));
		vPhaseStd = Math.sqrt(Math.log(1 + Math.pow(
				(Global.vPhaseDiffCoef / Global.vPhase), 2)));
		initialPositionX = x;
		initialPositionY = y;
		stats = new Statistics();
		// createMirrorAgent();
	}

	public MyxoAgent2(String paramLine, AgentSpace aSpace, SlimeSpace sSpace,
			ChemicalSpace cSpace, int cellID) {
		super();
		Scanner sc = new Scanner(paramLine).useDelimiter(" ");
		cellLength = sc.nextDouble();
		System.out.println(cellLength);
		cellWidth = sc.nextDouble();
		vAmoter = sc.nextDouble();
		vSmoter = sc.nextDouble();
		x = sc.nextDouble();
		y = sc.nextDouble();
		dir = sc.nextDouble() * 2 * Math.PI / 360;
		tagAgent();
		agentSpace = aSpace;
		slimeSpace = sSpace;
		cSignalSpace = cSpace;
		vPhase = Global.vPhase;
		vReproduct = Global.vReproduct;
		phaseReproduct = 2 * Math.PI * Global.r.nextDouble();
		setID(cellID);
		agentSpace = aSpace;
		// phase = 2 * Math.PI * Global.r.nextDouble();
		phase = 2.1;
		theta = dir > Math.PI ? dir - Math.PI : dir;
		headX = x + cellLength * Math.cos(dir) / 2.0;
		headY = y + cellLength * Math.sin(dir) / 2.0;
		tailX = x - cellLength * Math.cos(dir) / 2.0;
		tailY = y - cellLength * Math.sin(dir) / 2.0;
		dirOld = dir;
		gridX = (int) (x / agentSpace.getGridSize());
		gridY = (int) (y / agentSpace.getGridSize());
		headGridX = (int) (headX / agentSpace.getGridSize());
		headGridY = (int) (headY / agentSpace.getGridSize());
		phaseOld = phase;
		phaseReproduct = 2 * Math.PI * Global.r.nextDouble();
		tailGridX = (int) (tailX / agentSpace.getGridSize());
		tailGridY = (int) (tailY / agentSpace.getGridSize());
		thetaOld = theta;
		xOld = x;
		yOld = y;
		vMoterMean = Math.log(Global.vAmoter)
				- 0.5
				* Math.log(1 + Math.pow(
						(Global.velocityDiffCoef / Global.vAmoter), 2));
		vMoterStd = Math.sqrt(Math.log(1 + Math.pow(
				(Global.velocityDiffCoef / Global.vAmoter), 2)));
		vPhaseMean = Math.log(Global.vPhase)
				- 0.5
				* Math.log(1 + Math.pow(
						(Global.vPhaseDiffCoef / Global.vPhase), 2));
		vPhaseStd = Math.sqrt(Math.log(1 + Math.pow(
				(Global.vPhaseDiffCoef / Global.vPhase), 2)));
		initialPositionX = x;
		initialPositionY = y;
		Statistics stats = new Statistics();
		// createMirrorAgent();
	}

	public void step() {
		SIGNAL = Global.SIGNAL && cSignalSpace.ifExternaSignals(this);
		if (Global.DEBUG) {
			System.out.println("phase advance");
		}
		phaseAdvance();
		if (Global.DEBUG) {
			System.out.println("update speed");
		}
		updateVmoters();
		if (Global.DEBUG) {
			System.out.println("move agent");
		}
		// updateVphase();
		moveAgent();
		if (initialType < 4) {
			pBound(Global.worldX, Global.worldY);
		}
		if (initialType == 4) {
			coneBoundary();
		}
		agentStats();
		// createMirrorAgent();
	}

	public void updateAgentState() {
		ifReverse = false;
		COLLIDE = false;
		xOld = x;
		yOld = y;
		dirOld = dir;
		thetaOld = theta;
		headX = x + cellLength * Math.cos(dir) / 2.0;
		headY = y + cellLength * Math.sin(dir) / 2.0;
		tailX = x - cellLength * Math.cos(dir) / 2.0;
		tailY = y - cellLength * Math.sin(dir) / 2.0;
		gridX = (int) (x / agentSpace.getGridSize());
		gridY = (int) (y / agentSpace.getGridSize());
		headGridX = (int) (headX / agentSpace.getGridSize());
		headGridY = (int) (headY / agentSpace.getGridSize());
		tailGridX = (int) (tailX / agentSpace.getGridSize());
		tailGridY = (int) (tailY / agentSpace.getGridSize());
	}

	public void moveAgent() {
		if (AMOTER && !SMOTER) {
			moveByA();
		} else if (!AMOTER && SMOTER) {
			moveByS();
		} else if (AMOTER && SMOTER) {
			moveByBoth();
		} else {
			noMotility();
		}
	}

	/**
	 * Move agent with a-motor
	 */
	public void moveByA() {
		updateOrientation();
		x += vAmoter * Global.dT * Math.cos(dir) + 2
				* Math.sqrt(Global.xDiffCoef * Global.dT)
				* (Global.r.nextDouble() - 0.5);
		y += vAmoter * Global.dT * Math.sin(dir) + 2
				* Math.sqrt(Global.yDiffCoef * Global.dT)
				* (Global.r.nextDouble() - 0.5);
	}

	public void moveByS() {
		if (!ifAttached) {
			if (ifAttaching) {
				detach();
			} else {
				findAttachCell();
			}
			if (ifAttaching) {
				updateOrientation();
				int headJam = countCellInHead();
				x += (vSmoter * Global.dT * Math.cos(dir) + 2
						* Math.sqrt(Global.xDiffCoef * Global.dT)
						* (Global.r.nextDouble() - 0.5))
						/ ((double) (headJam + 1));
				y += (vSmoter * Global.dT * Math.sin(dir) + 2
						* Math.sqrt(Global.yDiffCoef * Global.dT)
						* (Global.r.nextDouble() - 0.5))
						/ ((double) (headJam + 1));
				pushCell(x - xOld, y - yOld);
			}
		}
	}

	public void moveByBoth() {
	}

	/**
	 * Update agent position if there is no motility available, only diffusion
	 */
	public void noMotility() {
		x += 2 * Math.sqrt(Global.xDiffCoef * Global.dT)
				* (Global.r.nextDouble() - 0.5);
		y += 2 * Math.sqrt(Global.yDiffCoef * Global.dT)
				* (Global.r.nextDouble() - 0.5);
	}

	/**
	 * This method deals with phase advance Check collision -> if collide then
	 * follow the collision resolve method -> else advance with phase velocity
	 */
	public void phaseAdvance() {
		phaseReproduct += vReproduct * Global.dT;
		if (SIGNAL && !COLLIDE) {
			checkCollision(Global.collisionType);
		}
		if (COLLIDE) {
			collisionResolve(Global.collisionResolveType);
		} else {
			phase += vPhase * Global.dT + 2
					* Math.sqrt(Global.phaseDiffCoef * Global.dT)
					* (Global.r.nextDouble() - 0.5);
		}
		if (phase >= 2 * Math.PI) {
			agentReverse();
		}
		if (phase < 0) {
			phase = 0;
		}
	}

	/**
	 * Check if current cell collides with other cells. Type 1 : center para dis
	 * < L/2 center perp dis < 2 * W and only +- dir counted Default type is
	 * Type 1.
	 * 
	 * @param type
	 */
	public void checkCollision(int type) {
		ArrayList<MyxoAgent> list = new ArrayList<MyxoAgent>();
		Point2D.Double center = new Point2D.Double(xOld, yOld);
		double samedir = dir;
		double oppdir = (dir + Math.PI) % (2 * Math.PI);
		if (Global.DEBUG) {
			// System.out.println("Same dir is " + dir + " oppdir is " +
			// oppdir);
		}
		switch (type) {
		case 1:
			list = agentSpace.getAgentList(xOld, yOld,
					Global.maxNeighborDis / 2);
			if (Global.DEBUG) {
				System.out.println("Local neighbor number " + list.size());
			}
			if (null != list) {
				for (int i = 0; i < list.size(); i++) {
					if (this == list.get(i)) {
						continue;
					}
					Point2D.Double center1 = new Point2D.Double(list.get(i)
							.getXOld(), list.get(i).getYOld());
					double centerDis = center.distance(center1);
					Line2D.Double ln = new Line2D.Double();
					ln.setLine(list.get(i).getHeadX(), list.get(i).getHeadY(),
							list.get(i).getTailX(), list.get(i).getTailY());
					double ptLdis = ln.ptLineDist(center);
					double disPara = Math.sqrt(centerDis * centerDis - ptLdis
							* ptLdis);
					if (Global.DEBUG) {
						System.out.println("Cell " + ID + " (" + xOld + ","
								+ yOld + ") " + " to cell "
								+ list.get(i).getID() + " ("
								+ list.get(i).getxOld() + ","
								+ list.get(i).getYOld() + ") " + ":");
						System.out.println("point to line distance: " + ptLdis);
						System.out.println("parallel distance: " + disPara);
					}
					if (disPara < cellLength / 2 && ptLdis < Global.windShield
							&& centerDis > 0) {
						if (Global.DEBUG) {
							System.out.println("Cell " + list.get(i).getID()
									+ " in distance range");
							System.out.println("Cell " + list.get(i).getID()
									+ " dir is " + list.get(i).getDirOld());
							System.out.println(oppdir
									+ " "
									+ list.get(i).getDirOld()
									+ " "
									+ MathUtils.inAngelRange(oppdir,
											Math.PI / 10, list.get(i)
													.getDirOld(), 2 * Math.PI));
						}
						if (MathUtils.inAngelRange(oppdir, Math.PI / 10, list
								.get(i).getDirOld(), 2 * Math.PI)) {
							if (Global.DEBUG) {
								System.out.println("Cell "
										+ list.get(i).getID()
										+ " in angle range");
							}
							if (list.get(i).isACTIVESIGNAL()) {
								COLLIDE = true;
							}
							if (ACTIVESIGNAL) {
								list.get(i).setCOLLIDE(true);
							}
						}
					}

				}
			}
			break;
		case 2:
			list = agentSpace.getHeadAgentList(headX, headY, Global.windShield);
			double x1 = headX - Global.windShield * Math.sin(dir) / 2;
			double x2 = headX + Global.windShield * Math.sin(dir) / 2;
			double y1 = headY + Global.windShield * Math.cos(dir) / 2;
			double y2 = headY - Global.windShield * Math.cos(dir) / 2;
			Line2D.Double windShield = new Line2D.Double(x1, y1, x2, y2);
			if (null != list) {
				for (int i = 0; i < list.size(); i++) {
					if (this == list.get(i)) {
						continue;
					}
					Point2D.Double headPoint = new Point2D.Double(list.get(i)
							.getHeadX(), list.get(i).getHeadY());
					Point2D.Double futureHeadPoint = new Point2D.Double(list
							.get(i).getHeadX()
							+ 2
							* list.get(i).getvAmoter()
							* Math.cos(list.get(i).getDirOld()), list.get(i)
							.getHeadY()
							+ 2
							* list.get(i).getvAmoter()
							* Math.sin(list.get(i).getDirOld()));
					Line2D.Double cellFrontPart = new Line2D.Double(headPoint,
							futureHeadPoint);
					if (MathUtils.inAngelRange(oppdir, Math.PI / 5, list.get(i)
							.getDirOld(), 2 * Math.PI)) {
						if (windShield.intersectsLine(cellFrontPart)) {
							if (list.get(i).isACTIVESIGNAL()) {
								COLLIDE = true;
							}
							if (ACTIVESIGNAL) {
								list.get(i).setCOLLIDE(true);
							}
						}
					}
				}
			}
			break;
		default:
			list = agentSpace.getAgentList(xOld, yOld,
					Global.maxNeighborDis / 2);
			if (Global.DEBUG) {
				System.out.println("Local neighbor number " + list.size());
			}
			if (null != list) {
				for (int i = 0; i < list.size(); i++) {
					if (this == list.get(i)) {
						continue;
					}
					Point2D.Double center1 = new Point2D.Double(list.get(i)
							.getXOld(), list.get(i).getYOld());
					double centerDis = center.distance(center1);
					Line2D.Double ln = new Line2D.Double();
					ln.setLine(list.get(i).getHeadX(), list.get(i).getHeadY(),
							list.get(i).getTailX(), list.get(i).getTailY());
					double ptLdis = ln.ptLineDist(center);
					double disPara = Math.sqrt(centerDis * centerDis - ptLdis
							* ptLdis);
					if (Global.DEBUG) {
						System.out.println("Cell " + ID + " (" + xOld + ","
								+ yOld + ") " + " to cell "
								+ list.get(i).getID() + " ("
								+ list.get(i).getxOld() + ","
								+ list.get(i).getYOld() + ") " + ":");
						System.out.println("point to line distance: " + ptLdis);
						System.out.println("parallel distance: " + disPara);
					}
					if (disPara < cellLength / 2 && ptLdis < Global.windShield
							&& centerDis > 0) {
						if (Global.DEBUG) {
							System.out.println("Cell " + list.get(i).getID()
									+ " in distance range");
							System.out.println("Cell " + list.get(i).getID()
									+ " dir is " + list.get(i).getDirOld());
							System.out.println(oppdir
									+ " "
									+ list.get(i).getDirOld()
									+ " "
									+ MathUtils.inAngelRange(oppdir,
											Math.PI / 10, list.get(i)
													.getDirOld(), 2 * Math.PI));
						}
						if (MathUtils.inAngelRange(oppdir, Math.PI / 10, list
								.get(i).getDirOld(), 2 * Math.PI)) {
							if (Global.DEBUG) {
								System.out.println("Cell "
										+ list.get(i).getID()
										+ " in angle range");
							}
							if (list.get(i).isACTIVESIGNAL()) {
								COLLIDE = true;
							}
							if (ACTIVESIGNAL) {
								list.get(i).setCOLLIDE(true);
							}
						}
					}

				}
			}
			break;
		}
	}

	public void collisionResolve(int type) {
		int rmapindex;
		double phaseJump;
		switch (type) {
		case 1:
			if (phase >= Global.refractoryPeriod
					&& Global.r.nextDouble() < Global.phaseJumpProbability) {
				phase += Global.phaseJump;
				Global.numCollisions += 1;
			} else {
				phase += vPhase * Global.dT + 2
						* Math.sqrt(Global.phaseDiffCoef * Global.dT)
						* (Global.r.nextDouble() - 0.5);
			}
			break;
		case 2:
			if (Global.r.nextDouble() < Global.phaseJumpProbability) {
				rmapindex = (int) (this.phase * Global.resettingMap.length / (2 * Math.PI));
				phaseJump = Global.resettingMap[rmapindex] * 2 * Math.PI;
				phase += phaseJump;
				Global.numCollisions += 1;
			} else {
				phase += vPhase * Global.dT + 2
						* Math.sqrt(Global.phaseDiffCoef * Global.dT)
						* (Global.r.nextDouble() - 0.5);
			}

			break;
		default:
			if (phase >= Global.refractoryPeriod) {
				Global.numCollisions += 1;
			}
			if (phase >= Global.refractoryPeriod
					&& Global.r.nextDouble() < Global.phaseJumpProbability) {
				phase += Global.phaseJump;
			} else {
				phase += vPhase * Global.dT + 2
						* Math.sqrt(Global.phaseDiffCoef * Global.dT)
						* (Global.r.nextDouble() - 0.5);
			}
			break;
		}
	}

	public void agentReverse() {
		phase = 0;
		ifReverse = true;
		reverseDir();
		if (SMOTER) {
			forceDetach();
		}
	}

	/**
	 * This method make sure dir follows p_boundary condition
	 */
	public void reverseDir() {
		dir += Math.PI;
		dir = (dir + 2 * Math.PI) % (2 * Math.PI);
	}

	/**
	 * Cell-cell alignment Cell turn to low density direction random turning
	 */
	public void updateOrientation() {
		if (Global.initialType == 4) {
			if (x > Global.originX + 0.5 * Global.iniRadius
					* Math.cos(Global.coneAngle / 2)
					&& x < Global.originX + 1.5 * Global.iniRadius
							* Math.cos(Global.coneAngle / 2)) {
				alignToAgents();
			}
		} else {
			if (Global.r.nextDouble() < Global.alignPro)
				alignToAgents();
		}
		if (Global.simType == 2) {
			double densityTurn = 0;
			double[] neighborIndex = stats.findNeighbor(this);
			double leftdensity = agentSpace.getLocalDensity(x + Global.vAmoter
					* Global.dT * Math.cos(dir) + neighborIndex[0], y
					+ Global.vAmoter * Global.dT * Math.sin(dir)
					+ neighborIndex[1], 1);
			double rightdensity = agentSpace.getLocalDensity(x + Global.vAmoter
					* Global.dT * Math.cos(dir) + neighborIndex[2], y
					+ Global.vAmoter * Global.dT * Math.sin(dir)
					+ neighborIndex[3], 1);

			if (leftdensity > rightdensity)
				densityTurn = 1;
			else
				densityTurn = -1;
			if (densityTurn != 0) {
				dir += densityTurn * Global.turn *Global.dT;
//						* Math.sqrt(2 * 25 * Global.dirDiffCoef * Global.dT)
//						* (Global.r.nextDouble() - 0.5);
			}
		}
		randomTurn();
		theta = dir > Math.PI ? dir - Math.PI : dir;
	}

	/**
	 * This method mekes the cell to align to the attached cell in one step. The
	 * new orietation is parallel to he attached cell direction.
	 */
	public void alignToAgentOneStep() {
		dir -= (theta - attachTo.getTheta());
	}

	/**
	 * This method makes the cell to align to the average alignment of its
	 * neighbors The neighbors are within one space grid
	 */
	public synchronized void alignToAgents() {
		if (AMOTER) {
			ArrayList<MyxoAgent> nList = agentSpace.getAgentList(headX, headY,
					1);
			double deltaTheta = 0;
			for (int i = 0; i < nList.size(); i++) {
				deltaTheta += alignToAngle(nList.get(i).getThetaOld());
			}
			if (nList.size() > 0) {
				deltaTheta /= (double) nList.size();
			}
			dir += deltaTheta;
		}
		if (SMOTER) {
			alignToAgentOneStep();
		}
	}

	/**
	 * This method calculate given an angle, how much the cell will align to
	 * that angle in one time step
	 * 
	 * @param alignTo
	 * @return
	 */
	public double alignToAngle(double alignTo) {
		double deltaTheta = 0;
		double gamma = theta - alignTo;
		double arcsin = Math.asin(Math.exp(-2 * vAmoter * Global.dT
				/ Global.cellLength + Math.log(Math.abs(Math.sin(gamma)))));

		if (gamma > 0 && gamma < Math.PI / 2) {
			deltaTheta = arcsin - gamma;
		} else if (gamma > Math.PI / 2 && gamma < Math.PI) {
			deltaTheta = Math.PI - arcsin - gamma;
		} else if (gamma > -Math.PI && gamma < -Math.PI / 2) {
			deltaTheta = -Math.PI + arcsin - gamma;
		} else if (gamma > -Math.PI / 2 && gamma < 0) {
			deltaTheta = -arcsin - gamma;
		}
		return deltaTheta;
	}

	/**
	 * Find a cell to attach to. The cell will pick up one cell within the
	 * interaction range of Global.maxNeighborDis to attach to.And the picked up
	 * cell should have minimum distance to the head point of the cell.
	 */
	public void findAttachCell() {
		double centerX = headX + (Global.maxNeighborDis - Global.cellWidth)
				* Math.cos(dir);
		double centerY = headY + (Global.maxNeighborDis - Global.cellWidth)
				* Math.sin(dir);
		ArrayList<MyxoAgent> list = agentSpace.getAgentList(centerX, centerY,
				Global.maxNeighborDis);
		Point2D.Double center = new Point2D.Double(headX, headY);
		double minDis = Global.maxNeighborDis * 2;
		int minID = 0;
		if (null != list) {
			for (int i = 0; i < list.size(); i++) {
				if (list.get(i).isIfAttaching()) {
					continue;
				}
				if (ifAttaching) {
					break;
				}
				if (list.get(i) == this) {
					continue;
				}
				Line2D.Double ln = new Line2D.Double();
				ln.setLine(list.get(i).getHeadX(), list.get(i).getHeadY(), list
						.get(i).getTailX(), list.get(i).getTailY());
				double ptSegdis = ln.ptSegDist(center);
				if (ptSegdis < Global.maxNeighborDis) {
					if (ptSegdis < minDis) {
						minDis = ptSegdis;
						minID = i;
					}
				}
			}
			if (Global.r.nextDouble() < Global.ACP
					&& minDis <= Global.maxNeighborDis) {
				attachTo = list.get(minID);
				list.get(minID).setIfAttached(true);
				ifAttaching = true;
			}
		}
	}

	/**
	 * Check if the cell is still valid for attaching
	 */
	public void detach() {
		double centerX = headX + (Global.maxNeighborDis - Global.cellWidth)
				* Math.cos(dir);
		double centerY = headY + (Global.maxNeighborDis - Global.cellWidth)
				* Math.sin(dir);
		Point2D.Double centerOwn = new Point2D.Double(centerX, centerY);
		Line2D.Double ln = new Line2D.Double();
		ln.setLine(attachTo.getHeadX(), attachTo.getHeadY(), attachTo
				.getTailX(), attachTo.getTailY());
		double ptSegDis = ln.ptSegDist(centerOwn);
		if (ptSegDis > Global.maxNeighborDis) {
			forceDetach();
		} else {
			if (Global.r.nextDouble() < Global.DCP) {
				forceDetach();
			}
		}
	}

	/**
	 * Detach immediately
	 */
	public void forceDetach() {
		ifAttaching = false;
		if (attachTo != null) {
			attachTo.setIfAttached(false);
		}
		attachTo = null;
	}

	/**
	 * Count how many cells are in the local area that is vSmoter * Global.dT
	 * away from cell head. The local area size (radius) is vSmoter * Global.dT
	 * 
	 * @return
	 */
	public int countCellInHead() {
		int cellCount = 0;
		double headCenterX = headX + vSmoter * Global.dT * Math.cos(dir);
		double headCenterY = headY + vSmoter * Global.dT * Math.sin(dir);
		double headInteractionX = headX + Global.maxNeighborDis * Math.cos(dir);
		double headInteractionY = headY + Global.maxNeighborDis * Math.sin(dir);
		ArrayList<MyxoAgent> list = agentSpace.getAgentList(headInteractionX,
				headInteractionY, Global.maxNeighborDis);
		Point2D.Double headLocal = new Point2D.Double(headCenterX, headCenterY);
		for (int i = 0; i < list.size(); i++) {
			if (list.get(i) == this) {
				continue;
			} else {
				Line2D.Double ln = new Line2D.Double(list.get(i).getHeadX(),
						list.get(i).getHeadY(), list.get(i).getTailX(), list
								.get(i).getTailY());
				if (ln.ptSegDist(headLocal) < vSmoter * Global.dT) {
					cellCount++;
				}
			}
		}
		return cellCount;
	}

	/**
	 * Find the cells in front of the cell. Count the number of cells in front
	 * to determine the cell movement.
	 * 
	 * @param dx
	 * @param dy
	 */
	public void pushCell(double dx, double dy) {
		ArrayList<MyxoAgent> list = agentSpace.getAgentList(headX, headY,
				Global.maxNeighborDis / 2);
		Point2D.Double center = new Point2D.Double(headX, headY);
		double samedir = dir;
		boolean pushing = false;
		double oppdir = (dir + Math.PI) % (2 * Math.PI);
		double minDis = 2 * Global.maxNeighborDis;
		int minID = 0;
		int neighborCellCount = 0;
		if (null != list) {
			for (int i = 0; i < list.size(); i++) {
				if (attachTo == list.get(i) || this == list.get(i)) {
					continue;
				}
				Point2D.Double center1 = new Point2D.Double(list.get(i)
						.getTailX(), list.get(i).getTailY());
				double centerDis = center.distance(center1);
				if (centerDis < Global.cellWidth) {
					if (MathUtils.inAngelRange(oppdir, Math.PI / 10, list
							.get(i).getDirOld(), 2 * Math.PI)
							|| MathUtils.inAngelRange(samedir, Math.PI / 10,
									list.get(i).getDirOld(), 2 * Math.PI)) {
						if (centerDis < minDis) {
							minDis = centerDis;
							minID = i;
							pushing = true;
						}
					}
				}
			}
		}

		if (pushing) {
			list.get(minID).setX(list.get(minID).getX() + dx / 2);
			list.get(minID).setY(list.get(minID).getY() + dy / 2);
		}
	}

	/**
	 * This method add noise to cell's orientation
	 */
	public void randomTurn() {
		dir += 2 * Math.sqrt(Global.dirDiffCoef * Global.dT)
				* (Global.r.nextDouble() - 0.5);
	}

	/**
	 * This method update moter velocities Each time step the velocity values
	 * are draw from a lognormal distribution with mean of Global.vAmoter and
	 * std of sqrt(vdiffco * dt)
	 */
	public void updateVmoters() {
		switch ((int) Global.simType) {
		case 1:
			break;
		case 2:
			// Slowing down according to local cell density with vChangepro
			if (Global.r.nextDouble() < Global.vChangePro) {
				double den = agentSpace.getLocalDensity(headX, headY,
						Global.densityRange);
				if (den > Global.densityThreshold) {
					ifInFb = true;
					vAmoter = Global.velocityChange * Global.vAmoter;
					inFbTimer += Global.dT;
					outFbTimer = 0;
				} else {
					ifInFb = false;
					vAmoter = Global.vAmoter;
					inFbTimer = 0;
					outFbTimer += Global.dT;
				}
			}
			break;
		default:
			break;
		}

		// vAmoter = Math.exp(vMoterMean + vMoterStd * Global.r.nextGaussian());
		// vSmoter = Math.exp(vMoterMean + vMoterStd * Global.r.nextGaussian());
	}

	/**
	 * This method update phase velocity Each time step the velocity values are
	 * draw from a lognormal distribution with mean of Global.vPhase and std of
	 * sqrt(vpdiffco*dt)
	 */
	public void updateVphase() {
		// vPhase = Math.exp(vPhaseMean + vPhaseStd * Global.r.nextGaussian());
	}

	public void tagAgent() {
		AMOTER = Global.AMOTOR;
		COLLIDE = false;
		DROPPED = false;
		HIGHDENSITY = false;
		IFALIGNED = Global.IFALIGNED;
		IFXBOUND = Global.PERBOUNDX;
		IFYBOUND = Global.PERBOUNDY;
		ININIAREA = true;
		INSLIME = false;
		NONEREVERSE = false;
		SIGNAL = Global.SIGNAL;
		if (Global.r.nextDouble() < Global.positiveSignalFraction) {
			ACTIVESIGNAL = Global.SIGNAL;
		} else {
			ACTIVESIGNAL = false;
		}
		SMOTER = Global.SMOTOR;
		ifReverse = false;
		trackLabel = false;
		reverseTimer = 0;
		refTimer = 0;
		distancePerReverse = 0;
		ifInRef = false;
		numReverse = 0;
		numInRef = 0;
		xDisFromIni = 0;
		yDisFromIni = 0;
		xDisFromMean = 0;
		yDisFromMean = 0;
		xDisFromRevStart = 0;
		xDisFromRevStart = 0;
		flagCrossBoundary = 0;
		INCONSTDENRANGE = true;
		IFDROPPED = false;
		attachTo = null;
		ifAttached = false;
		ifAttaching = false;
	}

	public void pBound(double px, double py) {
		if (Global.PERBOUNDX) {
			x = (x + px) % px;
		} else {
			if (x > Global.worldX || x < 0) {
				IFDROPPED = true;
			} else {
				IFDROPPED = false;
			}
		}
		if (Global.PERBOUNDY) {
			y = (y + py) % py;
		}
		if (x <= Global.iniWidth && x >= 0) {
			INCONSTDENRANGE = true;
		} else {
			INCONSTDENRANGE = false;
		}
	}

	// The special two cone boundary condition
	public void coneBoundary() {

		// First do the rectangle periodic boundary condition
		if (x < Global.originX) {
			x = 2 * Global.originX - x;
		}
		if (y < Global.originY - Global.iniRadius
				* Math.sin(Global.coneAngle / 2)) {
			y = 2
					* (Global.originY - Global.iniRadius
							* Math.sin(Global.coneAngle / 2)) - y;
		}
		if (x > Global.originX + 2 * Global.iniRadius
				* Math.cos(Global.coneAngle / 2)) {
			x = 2
					* (Global.originX + 2 * Global.iniRadius
							* Math.cos(Global.coneAngle / 2)) - x;
		}
		if (y > Global.originY + Global.iniRadius
				* Math.sin(Global.coneAngle / 2)) {
			y = 2
					* (Global.originY + Global.iniRadius
							* Math.sin(Global.coneAngle / 2)) - y;
		}
		// For the left cone
		if (x < Global.originX + Global.iniRadius
				* Math.cos(Global.coneAngle / 2)) {
			// up
			if (y - Global.originY > (x - Global.originX)
					* Math.tan(Global.coneAngle / 2)) {
				y -= 2 * (x - Global.originX) * Math.tan(Global.coneAngle / 2);
				dir = dir - Global.coneAngle;
			}
			while (y - Global.originY > (x - Global.originX)
					* Math.tan(Global.coneAngle / 2)) {
				y -= 2 * (x - Global.originX) * Math.tan(Global.coneAngle / 2);
			}
			// low
			if (Global.originY - y > (x - Global.originX)
					* Math.tan(Global.coneAngle / 2)) {
				y += 2 * (x - Global.originX) * Math.tan(Global.coneAngle / 2);
				dir = dir + Global.coneAngle;
			}
			while (Global.originY - y > (x - Global.originX)
					* Math.tan(Global.coneAngle / 2)) {
				y += 2 * (x - Global.originX) * Math.tan(Global.coneAngle / 2);
			}
		}
		// For the right cone
		else {
			// up
			if (y - Global.originY > (2 * Global.iniRadius
					* Math.cos(Global.coneAngle / 2) - x)
					* Math.tan(Global.coneAngle / 2)) {
				y -= 2
						* (2 * Global.iniRadius
								* Math.cos(Global.coneAngle / 2) - x)
						* Math.tan(Global.coneAngle / 2);
				dir = dir + Global.coneAngle;
			}
			while (y - Global.originY > (2 * Global.iniRadius
					* Math.cos(Global.coneAngle / 2) - x)
					* Math.tan(Global.coneAngle / 2)) {
				y -= 2
						* (2 * Global.iniRadius
								* Math.cos(Global.coneAngle / 2) - x)
						* Math.tan(Global.coneAngle / 2);
			}
			// low
			if (Global.originY - y > (2 * Global.iniRadius
					* Math.cos(Global.coneAngle / 2) - x)
					* Math.tan(Global.coneAngle / 2)) {
				y += (2 * Global.iniRadius * Math.cos(Global.coneAngle / 2) - x)
						* Math.tan(Global.coneAngle / 2);
				dir = dir - Global.coneAngle;
			}
			while (Global.originY - y > (2 * Global.iniRadius
					* Math.cos(Global.coneAngle / 2) - x)
					* Math.tan(Global.coneAngle / 2)) {
				y += (2 * Global.iniRadius * Math.cos(Global.coneAngle / 2) - x)
						* Math.tan(Global.coneAngle / 2);
			}
		}

	}

	public void statsInit() {
		meanPositionX = x;
		meanPositionY = y;
		initialPositionX = x;
		initialPositionY = y;
	}

	public void generalStatInit() {
		generalIniX = x;
		generalIniY = y;
		generalMeanX = x;
		generalMeanY = y;
	}

	public void agentStats() {
		// Mean square displacement
		xDisFromIni = x - initialPositionX;
		yDisFromIni = y - initialPositionY;
		xDisFromMean = x - meanPositionX;
		yDisFromMean = y - meanPositionY;
		xDisFromRevStart = x - revStartX;
		yDisFromRevStart = y - revStartY;
		generalXDisFromIni = x - generalIniX;
		generalYDisFromIni = y - generalIniY;
		generalXDisFromMean = x - generalMeanX;
		generalYDisFromMean = y - generalMeanY;
		meanPositionX = (((double) Global.timeIndex - Global.trackStartTime
				/ Global.dT + 1)
				* meanPositionX + x)
				/ ((double) Global.timeIndex - Global.trackStartTime
						/ Global.dT + 1 + 1);
		meanPositionY = (((double) Global.timeIndex - Global.trackStartTime
				/ Global.dT + 1)
				* meanPositionY + y)
				/ ((double) Global.timeIndex - Global.trackStartTime
						/ Global.dT + 1 + 1);
		if (Global.currTime < Global.simTime / 3) {
			generalMeanX = (((double) Global.timeIndex - 0 + 1) * generalMeanX + x)
					/ ((double) Global.timeIndex + 2);
			generalMeanY = (((double) Global.timeIndex - 0 + 1) * generalMeanY + y)
					/ ((double) Global.timeIndex + 2);
		} else if (Global.currTime < Global.simTime * 2 / 3) {
			generalMeanX = (((double) Global.timeIndex - Global.simTime
					/ (3 * Global.dT) + 1)
					* meanPositionX + x)
					/ ((double) Global.timeIndex - Global.simTime
							/ (3 * Global.dT) + 2);
			meanPositionY = (((double) Global.timeIndex - Global.simTime
					/ (3 * Global.dT) + 1)
					* meanPositionY + y)
					/ ((double) Global.timeIndex - Global.simTime
							/ (3 * Global.dT) + 2);
		} else {
			generalMeanX = (((double) Global.timeIndex - Global.simTime * 2
					/ (3 * Global.dT) + 1)
					* meanPositionX + x)
					/ ((double) Global.timeIndex - Global.simTime * 2
							/ (3 * Global.dT) + 1 + 1);
			meanPositionY = (((double) Global.timeIndex - Global.simTime * 2
					/ (3 * Global.dT) + 1)
					* meanPositionY + y)
					/ ((double) Global.timeIndex - Global.simTime * 2
							/ (3 * Global.dT) + 1 + 1);
		}
		if (Global.currTime > Global.trackStartTime
				&& Global.currTime < Global.trackEndTime) {
			updateReversalTimeList();
			updateRefractoryTimeList();
		}
		checkCrossFluxBoundary();

		// Adding cell velocity to calculate average cell velocity
		Global.vArray[Global.timeIndex] += vAmoter;
	}

	/**
	 * Sub routine of statistics function to calculate reversal time statistics
	 * Also update the distance between reversals.
	 */
	public void updateReversalTimeList() {
		if (ifReverse) {
			numReverse += 1;
			if (Global.DEBUG) {
				System.out.println("Cell " + ID + " Reversing number: "
						+ numReverse);
			}
			if (numReverse >= 2) {
				Global.reverPeriods.add((Double) reverseTimer);
				if (Global.DEBUG) {
					System.out.println("Reversing time: " + reverseTimer);
				}
				Global.distancePerReverse.add((Double) distancePerReverse);
				if (Global.DEBUG) {
					System.out.println("Reversing distance " + reverseTimer);
				}
			}
			ifReverse = false;
			reverseTimer = 0;
			distancePerReverse = 0;
		} else {
			reverseTimer += Global.dT;
			distancePerReverse += vAmoter * Global.dT;
		}
	}

	/**
	 * Sub routine of statistics function to calculate refractory time
	 * statistics.
	 */
	public void updateRefractoryTimeList() {
		if (phase < Global.refractoryPeriod) {
			if (ifInRef) {
				refTimer += Global.dT;
			} else {
				ifInRef = true;
				numInRef += 1;
				refTimer = Global.dT;
				// System.out.println(numInRef + " " + phase
				// / Global.refractoryPeriod);
			}
		} else {
			if (ifInRef) {
				if (numInRef >= 2) {
					Global.refractorTime.add(refTimer);
					// System.out.println(refTimer + " " + numInRef + " "
					// + phase / Global.refractoryPeriod);
				}
				ifInRef = false;
			}
		}
	}

	public void checkCrossFluxBoundary() {
		switch (Global.fluxType) {
		case 1:
			if (x > Global.fluxBoundary && xOld < Global.fluxBoundary) {
				flagCrossBoundary = 1;
			}
			if (x < Global.fluxBoundary && xOld > Global.fluxBoundary) {
				flagCrossBoundary = 2;
			}
			break;
		case 2:
			Rectangle2D.Double fluxRegionRec = new Rectangle2D.Double(
					Global.boundX, Global.boundY, Global.boundWidth,
					Global.boundHeight);
			if (!fluxRegionRec.contains(x, y)
					&& fluxRegionRec.contains(xOld, yOld)) {
				flagCrossBoundary = 1;
			}
			if (fluxRegionRec.contains(x, y)
					&& !fluxRegionRec.contains(xOld, yOld)) {
				flagCrossBoundary = 2;
			}
			break;
		case 3:
			Ellipse2D.Double fluxRegionRound = new Ellipse2D.Double(
					Global.boundCenterX - Global.boundR, Global.boundCenterY
							- Global.boundR, 2 * Global.boundR,
					2 * Global.boundR);
			if (!fluxRegionRound.contains(x, y)
					&& fluxRegionRound.contains(xOld, yOld)) {
				flagCrossBoundary = 1;
			}
			if (fluxRegionRound.contains(x, y)
					&& fluxRegionRound.contains(xOld, yOld)) {
				flagCrossBoundary = 2;
			}
			break;
		default:
			if (x > Global.fluxBoundary && xOld < Global.fluxBoundary) {
				flagCrossBoundary = 1;
			}
			if (x < Global.fluxBoundary && xOld > Global.fluxBoundary) {
				flagCrossBoundary = 2;
			}
			break;
		}
	}

	public void createMirrorAgent() {

		// Bottom to up
		if (y < Global.maxNeighborDis) {
			MyxoAgent m;
			try {
				m = (MyxoAgent) this.clone();
				m.setY(y + Global.worldY);
				Global.mirrorAgents.add(m);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}

		// up to bottom
		if (y > Global.worldY - Global.maxNeighborDis) {
			MyxoAgent m;
			try {
				m = (MyxoAgent) this.clone();
				m.setY(y - Global.worldY);
				Global.mirrorAgents.add(m);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}

		// left to right
		if (x < Global.maxNeighborDis) {
			MyxoAgent m;
			try {
				m = (MyxoAgent) this.clone();
				m.setX(x + Global.worldX);
				Global.mirrorAgents.add(m);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}

		// right to left
		if (x > Global.worldX - Global.maxNeighborDis) {
			MyxoAgent m;
			try {
				m = (MyxoAgent) this.clone();
				m.setX(x - Global.worldX);
				Global.mirrorAgents.add(m);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}

		// four corners
		if (x < Global.maxNeighborDis && y < Global.maxNeighborDis) {
			MyxoAgent m;
			try {
				m = (MyxoAgent) this.clone();
				m.setX(x + Global.worldX);
				m.setY(y + Global.worldY);
				Global.mirrorAgents.add(m);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}
		if (x < Global.maxNeighborDis
				&& y > Global.worldY - Global.maxNeighborDis) {
			MyxoAgent m;
			try {
				m = (MyxoAgent) this.clone();
				m.setX(x + Global.worldX);
				m.setY(y - Global.worldY);
				Global.mirrorAgents.add(m);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}
		if (x > Global.worldX - Global.maxNeighborDis
				&& y < Global.maxNeighborDis) {
			MyxoAgent m;
			try {
				m = (MyxoAgent) this.clone();
				m.setX(x - Global.worldX);
				m.setY(y + Global.worldY);
				Global.mirrorAgents.add(m);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}
		if (x > Global.worldX - Global.maxNeighborDis
				&& y > Global.worldY - Global.maxNeighborDis) {
			MyxoAgent m;
			try {
				m = (MyxoAgent) this.clone();
				m.setX(x - Global.worldX);
				m.setY(y - Global.worldY);
				Global.mirrorAgents.add(m);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}
	}

	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}

	private boolean IFALIGNED;

	private boolean AMOTER;

	private boolean SMOTER;

	private boolean SIGNAL;

	private boolean ACTIVESIGNAL;

	private boolean COLLIDE;

	private boolean NONEREVERSE;

	private boolean IFXBOUND;

	private boolean IFXBOUNDE;

	private boolean IFXBOUNDM;

	private boolean IFXBOUNDL;

	private boolean IFYBOUND;

	private boolean IFXLEFTIN;

	private boolean IFXRIGHTIN;

	private boolean IFXLEFTOUT;

	private boolean IFXRIGHTOUT;

	private boolean ININIAREA;

	private boolean DROPPED;

	private boolean INSLIME;

	private boolean HIGHDENSITY;

	private boolean trackLabel;

	private boolean generalTrack;

	private boolean INCONSTDENRANGE;

	private boolean IFDROPPED;

	private boolean ifAttached;

	private boolean ifAttaching;

	private boolean ifInFb;

	private int ID;

	private int initialType;

	private int gridX;

	private int gridY;

	private int headGridX;

	private int headGridY;

	private int tailGridX;

	private int tailGridY;

	private int flagCrossBoundary;

	private double x;

	private double xOld;

	private double y;

	private double yOld;

	private double headX;

	private double headY;

	private double tailX;

	private double tailY;

	private double dir;

	private double dirOld;

	private double theta;

	private double thetaOld;

	private double cellLength;

	private double cellWidth;

	private double vAmoter;

	private double vSmoter;

	private double vPhase;

	private double phase;

	private double phaseOld;

	private double vReproduct;

	private double phaseReproduct;

	private double vPhaseMean;

	private double vPhaseStd;

	private double vMoterMean;

	private double vMoterStd;

	private MyxoAgent attachTo;

	private Global global;

	private AgentSpace agentSpace;

	private SlimeSpace slimeSpace;

	private ChemicalSpace cSignalSpace;

	// Parameter related to statistics
	private boolean ifReverse;

	private int numReverse;

	private double reverseTimer;

	private boolean ifInRef;

	private int numInRef;

	private double refTimer;

	private double inFbTimer;

	private double outFbTimer;

	private double initialPositionX;

	private double initialPositionY;

	private double meanPositionX;

	private double meanPositionY;

	private double generalIniX;

	private double generalIniY;

	private double generalMeanX;

	private double generalMeanY;

	private double revStartX;

	private double revStartY;

	private double xDisFromIni;

	private double yDisFromIni;

	private double xDisFromMean;

	private double yDisFromMean;

	private double generalXDisFromIni;

	private double generalYDisFromIni;

	private double generalXDisFromMean;

	private double generalYDisFromMean;

	private double xDisFromRevStart;

	private double yDisFromRevStart;

	private double distancePerReverse;

	private Statistics stats;

	public int getFlagCrossBoundary() {
		return flagCrossBoundary;
	}

	public void setFlagCrossBoundary(int flagCrossBoundary) {
		this.flagCrossBoundary = flagCrossBoundary;
	}

	/**
	 * @return the cellWidth
	 */
	public double getCellWidth() {
		return cellWidth;
	}

	/**
	 * @param cellWidth
	 *            the cellWidth to set
	 */
	public void setCellWidth(double cellWidth) {
		this.cellWidth = cellWidth;
	}

	/**
	 * @return the cSignalSpace
	 */
	public ChemicalSpace getCSignalSpace() {
		return cSignalSpace;
	}

	/**
	 * @param signalSpace
	 *            the cSignalSpace to set
	 */
	public void setCSignalSpace(ChemicalSpace signalSpace) {
		cSignalSpace = signalSpace;
	}

	public AgentSpace getAgentSpace() {
		return agentSpace;
	}

	public void setAgentSpace(AgentSpace agentSpace) {
		this.agentSpace = agentSpace;
	}

	public MyxoAgent getAttachTo() {
		return attachTo;
	}

	public void setAttachTo(MyxoAgent attachTo) {
		this.attachTo = attachTo;
	}

	public ChemicalSpace getcSignalSpace() {
		return cSignalSpace;
	}

	public void setcSignalSpace(ChemicalSpace cSignalSpace) {
		this.cSignalSpace = cSignalSpace;
	}

	public boolean isIfAttached() {
		return ifAttached;
	}

	public void setIfAttached(boolean ifAttached) {
		this.ifAttached = ifAttached;
	}

	public boolean isIfAttaching() {
		return ifAttaching;
	}

	public void setIfAttaching(boolean ifAttaching) {
		this.ifAttaching = ifAttaching;
	}

	public SlimeSpace getSlimeSpace() {
		return slimeSpace;
	}

	public void setSlimeSpace(SlimeSpace slimeSpace) {
		this.slimeSpace = slimeSpace;
	}

	public double getvAmoter() {
		return vAmoter;
	}

	public void setvAmoter(double vAmoter) {
		this.vAmoter = vAmoter;
	}

	public double getvMoterMean() {
		return vMoterMean;
	}

	public void setvMoterMean(double vMoterMean) {
		this.vMoterMean = vMoterMean;
	}

	public double getvMoterStd() {
		return vMoterStd;
	}

	public void setvMoterStd(double vMoterStd) {
		this.vMoterStd = vMoterStd;
	}

	public double getvPhase() {
		return vPhase;
	}

	public void setvPhase(double vPhase) {
		this.vPhase = vPhase;
	}

	public double getvPhaseMean() {
		return vPhaseMean;
	}

	public void setvPhaseMean(double vPhaseMean) {
		this.vPhaseMean = vPhaseMean;
	}

	public double getvPhaseStd() {
		return vPhaseStd;
	}

	public void setvPhaseStd(double vPhaseStd) {
		this.vPhaseStd = vPhaseStd;
	}

	public double getvReproduct() {
		return vReproduct;
	}

	public void setvReproduct(double vReproduct) {
		this.vReproduct = vReproduct;
	}

	public double getvSmoter() {
		return vSmoter;
	}

	public void setvSmoter(double vSmoter) {
		this.vSmoter = vSmoter;
	}

	public double getxDisFromIni() {
		return xDisFromIni;
	}

	public void setxDisFromIni(double xDisFromIni) {
		this.xDisFromIni = xDisFromIni;
	}

	public double getxDisFromMean() {
		return xDisFromMean;
	}

	public void setxDisFromMean(double xDisFromMean) {
		this.xDisFromMean = xDisFromMean;
	}

	public double getxDisFromRevStart() {
		return xDisFromRevStart;
	}

	public void setxDisFromRevStart(double xDisFromRevStart) {
		this.xDisFromRevStart = xDisFromRevStart;
	}

	public double getxOld() {
		return xOld;
	}

	public void setxOld(double xOld) {
		this.xOld = xOld;
	}

	public double getyDisFromIni() {
		return yDisFromIni;
	}

	public void setyDisFromIni(double yDisFromIni) {
		this.yDisFromIni = yDisFromIni;
	}

	public double getyDisFromMean() {
		return yDisFromMean;
	}

	public void setyDisFromMean(double yDisFromMean) {
		this.yDisFromMean = yDisFromMean;
	}

	public double getyDisFromRevStart() {
		return yDisFromRevStart;
	}

	public void setyDisFromRevStart(double yDisFromRevStart) {
		this.yDisFromRevStart = yDisFromRevStart;
	}

	/**
	 * @return the iFDROPPED
	 */
	public boolean isIFDROPPED() {
		return IFDROPPED;
	}

	/**
	 * @param ifdropped
	 *            the iFDROPPED to set
	 */
	public void setIFDROPPED(boolean ifdropped) {
		IFDROPPED = ifdropped;
	}

	/**
	 * @return the iNCONSTDENRANGE
	 */
	public boolean isINCONSTDENRANGE() {
		return INCONSTDENRANGE;
	}

	/**
	 * @param inconstdenrange
	 *            the iNCONSTDENRANGE to set
	 */
	public void setINCONSTDENRANGE(boolean inconstdenrange) {
		INCONSTDENRANGE = inconstdenrange;
	}

	/**
	 * @return the ifInRef
	 */
	public boolean isIfInRef() {
		return ifInRef;
	}

	/**
	 * @param ifInRef
	 *            the ifInRef to set
	 */
	public void setIfInRef(boolean ifInRef) {
		this.ifInRef = ifInRef;
	}

	/**
	 * @return the numInRef
	 */
	public int getNumInRef() {
		return numInRef;
	}

	/**
	 * @param numInRef
	 *            the numInRef to set
	 */
	public void setNumInRef(int numInRef) {
		this.numInRef = numInRef;
	}

	/**
	 * @return the refTimer
	 */
	public double getRefTimer() {
		return refTimer;
	}

	/**
	 * @param refTimer
	 *            the refTimer to set
	 */
	public void setRefTimer(double refTimer) {
		this.refTimer = refTimer;
	}

	/**
	 * @return the aCTIVESIGNAL
	 */
	public boolean isACTIVESIGNAL() {
		return ACTIVESIGNAL;
	}

	/**
	 * @param activesignal
	 *            the aCTIVESIGNAL to set
	 */
	public void setACTIVESIGNAL(boolean activesignal) {
		ACTIVESIGNAL = activesignal;
	}

	/**
	 * @return the distancePerReverse
	 */
	public double getDistancePerReverse() {
		return distancePerReverse;
	}

	/**
	 * @param distancePerReverse
	 *            the distancePerReverse to set
	 */
	public void setDistancePerReverse(double distancePerReverse) {
		this.distancePerReverse = distancePerReverse;
	}

	/**
	 * @return the iFALIGNED
	 */
	public boolean isIFALIGNED() {
		return IFALIGNED;
	}

	/**
	 * @param ifaligned
	 *            the iFALIGNED to set
	 */
	public void setIFALIGNED(boolean ifaligned) {
		IFALIGNED = ifaligned;
	}

	/**
	 * @return the sIGNAL
	 */
	public boolean isSIGNAL() {
		return SIGNAL;
	}

	/**
	 * @param signal
	 *            the sIGNAL to set
	 */
	public void setSIGNAL(boolean signal) {
		SIGNAL = signal;
	}

	/**
	 * @return the cOLLIDE
	 */
	public boolean isCOLLIDE() {
		return COLLIDE;
	}

	/**
	 * @param collide
	 *            the cOLLIDE to set
	 */
	public void setCOLLIDE(boolean collide) {
		COLLIDE = collide;
	}

	/**
	 * @return the nONEREVERSE
	 */
	public boolean isNONEREVERSE() {
		return NONEREVERSE;
	}

	/**
	 * @param nonereverse
	 *            the nONEREVERSE to set
	 */
	public void setNONEREVERSE(boolean nonereverse) {
		NONEREVERSE = nonereverse;
	}

	/**
	 * @return the iFXBOUND
	 */
	public boolean isIFXBOUND() {
		return IFXBOUND;
	}

	/**
	 * @param ifxbound
	 *            the iFXBOUND to set
	 */
	public void setIFXBOUND(boolean ifxbound) {
		IFXBOUND = ifxbound;
	}

	/**
	 * @return the iFXBOUNDE
	 */
	public boolean isIFXBOUNDE() {
		return IFXBOUNDE;
	}

	/**
	 * @return the iFYBOUND
	 */
	public boolean isIFYBOUND() {
		return IFYBOUND;
	}

	/**
	 * @param ifybound
	 *            the iFYBOUND to set
	 */
	public void setIFYBOUND(boolean ifybound) {
		IFYBOUND = ifybound;
	}

	/**
	 * @param ifxbounde
	 *            the iFXBOUNDE to set
	 */
	public void setIFXBOUNDE(boolean ifxbounde) {
		IFXBOUNDE = ifxbounde;
	}

	/**
	 * @return the iFXBOUNDM
	 */
	public boolean isIFXBOUNDM() {
		return IFXBOUNDM;
	}

	/**
	 * @param ifxboundm
	 *            the iFXBOUNDM to set
	 */
	public void setIFXBOUNDM(boolean ifxboundm) {
		IFXBOUNDM = ifxboundm;
	}

	/**
	 * @return the iFXBOUNDL
	 */
	public boolean isIFXBOUNDL() {
		return IFXBOUNDL;
	}

	/**
	 * @param ifxboundl
	 *            the iFXBOUNDL to set
	 */
	public void setIFXBOUNDL(boolean ifxboundl) {
		IFXBOUNDL = ifxboundl;
	}

	/**
	 * @return the iFXLEFTIN
	 */
	public boolean isIFXLEFTIN() {
		return IFXLEFTIN;
	}

	/**
	 * @param ifxleftin
	 *            the iFXLEFTIN to set
	 */
	public void setIFXLEFTIN(boolean ifxleftin) {
		IFXLEFTIN = ifxleftin;
	}

	/**
	 * @return the iFXRIGHTIN
	 */
	public boolean isIFXRIGHTIN() {
		return IFXRIGHTIN;
	}

	/**
	 * @param ifxrightin
	 *            the iFXRIGHTIN to set
	 */
	public void setIFXRIGHTIN(boolean ifxrightin) {
		IFXRIGHTIN = ifxrightin;
	}

	/**
	 * @return the iFXLEFTOUT
	 */
	public boolean isIFXLEFTOUT() {
		return IFXLEFTOUT;
	}

	/**
	 * @param ifxleftout
	 *            the iFXLEFTOUT to set
	 */
	public void setIFXLEFTOUT(boolean ifxleftout) {
		IFXLEFTOUT = ifxleftout;
	}

	/**
	 * @return the iFXRIGHTOUT
	 */
	public boolean isIFXRIGHTOUT() {
		return IFXRIGHTOUT;
	}

	/**
	 * @param ifxrightout
	 *            the iFXRIGHTOUT to set
	 */
	public void setIFXRIGHTOUT(boolean ifxrightout) {
		IFXRIGHTOUT = ifxrightout;
	}

	/**
	 * @return the iNINIAREA
	 */
	public boolean isININIAREA() {
		return ININIAREA;
	}

	/**
	 * @param ininiarea
	 *            the iNINIAREA to set
	 */
	public void setININIAREA(boolean ininiarea) {
		ININIAREA = ininiarea;
	}

	/**
	 * @return the dROPPED
	 */
	public boolean isDROPPED() {
		return DROPPED;
	}

	/**
	 * @param dropped
	 *            the dROPPED to set
	 */
	public void setDROPPED(boolean dropped) {
		DROPPED = dropped;
	}

	/**
	 * @return the iNSLIME
	 */
	public boolean isINSLIME() {
		return INSLIME;
	}

	/**
	 * @param inslime
	 *            the iNSLIME to set
	 */
	public void setINSLIME(boolean inslime) {
		INSLIME = inslime;
	}

	/**
	 * @return the hIGHDENSITY
	 */
	public boolean isHIGHDENSITY() {
		return HIGHDENSITY;
	}

	/**
	 * @param highdensity
	 *            the hIGHDENSITY to set
	 */
	public void setHIGHDENSITY(boolean highdensity) {
		HIGHDENSITY = highdensity;
	}

	/**
	 * @return the aMOTER
	 */
	public boolean isAMOTER() {
		return AMOTER;
	}

	/**
	 * @param amoter
	 *            the aMOTER to set
	 */
	public void setAMOTER(boolean amoter) {
		AMOTER = amoter;
	}

	/**
	 * @return the sMOTER
	 */
	public boolean isSMOTER() {
		return SMOTER;
	}

	/**
	 * @param smoter
	 *            the sMOTER to set
	 */
	public void setSMOTER(boolean smoter) {
		SMOTER = smoter;
	}

	/**
	 * @return the gridX
	 */
	public int getGridX() {
		return gridX;
	}

	/**
	 * @param gridX
	 *            the gridX to set
	 */
	public void setGridX(int gridX) {
		this.gridX = gridX;
	}

	/**
	 * @return the gridY
	 */
	public int getGridY() {
		return gridY;
	}

	/**
	 * @param gridY
	 *            the gridY to set
	 */
	public void setGridY(int gridY) {
		this.gridY = gridY;
	}

	/**
	 * @return the headGridX
	 */
	public int getHeadGridX() {
		return headGridX;
	}

	/**
	 * @param headGridX
	 *            the headGridX to set
	 */
	public void setHeadGridX(int headGridX) {
		this.headGridX = headGridX;
	}

	/**
	 * @return the headGridY
	 */
	public int getHeadGridY() {
		return headGridY;
	}

	/**
	 * @param headGridY
	 *            the headGridY to set
	 */
	public void setHeadGridY(int headGridY) {
		this.headGridY = headGridY;
	}

	/**
	 * @return the tailGridX
	 */
	public int getTailGridX() {
		return tailGridX;
	}

	/**
	 * @param tailGridX
	 *            the tailGridX to set
	 */
	public void setTailGridX(int tailGridX) {
		this.tailGridX = tailGridX;
	}

	/**
	 * @return the tailGridY
	 */
	public int getTailGridY() {
		return tailGridY;
	}

	/**
	 * @param tailGridY
	 *            the tailGridY to set
	 */
	public void setTailGridY(int tailGridY) {
		this.tailGridY = tailGridY;
	}

	/**
	 * @return the x
	 */
	public double getX() {
		return x;
	}

	/**
	 * @param x
	 *            the x to set
	 */
	public void setX(double x) {
		this.x = x;
	}

	/**
	 * @return the y
	 */
	public double getY() {
		return y;
	}

	/**
	 * @param y
	 *            the y to set
	 */
	public void setY(double y) {
		this.y = y;
	}

	/**
	 * @return the headX
	 */
	public double getHeadX() {
		return headX;
	}

	/**
	 * @param headX
	 *            the headX to set
	 */
	public void setHeadX(double headX) {
		this.headX = headX;
	}

	/**
	 * @return the headY
	 */
	public double getHeadY() {
		return headY;
	}

	/**
	 * @param headY
	 *            the headY to set
	 */
	public void setHeadY(double headY) {
		this.headY = headY;
	}

	/**
	 * @return the tailX
	 */
	public double getTailX() {
		return tailX;
	}

	/**
	 * @param tailX
	 *            the tailX to set
	 */
	public void setTailX(double tailX) {
		this.tailX = tailX;
	}

	/**
	 * @return the tailY
	 */
	public double getTailY() {
		return tailY;
	}

	/**
	 * @param tailY
	 *            the tailY to set
	 */
	public void setTailY(double tailY) {
		this.tailY = tailY;
	}

	/**
	 * @return the cellLength
	 */
	public double getCellLength() {
		return cellLength;
	}

	/**
	 * @param cellLength
	 *            the cellLength to set
	 */
	public void setCellLength(double cellLength) {
		this.cellLength = cellLength;
	}

	/**
	 * @return the vAmoter
	 */
	public double getVAmoter() {
		return vAmoter;
	}

	/**
	 * @param amoter
	 *            the vAmoter to set
	 */
	public void setVAmoter(double amoter) {
		vAmoter = amoter;
	}

	/**
	 * @return the vSmoter
	 */
	public double getVSmoter() {
		return vSmoter;
	}

	/**
	 * @param smoter
	 *            the vSmoter to set
	 */
	public void setVSmoter(double smoter) {
		vSmoter = smoter;
	}

	/**
	 * @return the vPhase
	 */
	public double getVPhase() {
		return vPhase;
	}

	/**
	 * @param phase
	 *            the vPhase to set
	 */
	public void setVPhase(double phase) {
		vPhase = phase;
	}

	/**
	 * @return the phase
	 */
	public double getPhase() {
		return phase;
	}

	/**
	 * @param phase
	 *            the phase to set
	 */
	public void setPhase(double phase) {
		this.phase = phase;
	}

	/**
	 * @return the vReproduct
	 */
	public double getVReproduct() {
		return vReproduct;
	}

	/**
	 * @param reproduct
	 *            the vReproduct to set
	 */
	public void setVReproduct(double reproduct) {
		vReproduct = reproduct;
	}

	/**
	 * @return the phaseReproduct
	 */
	public double getPhaseReproduct() {
		return phaseReproduct;
	}

	/**
	 * @param phaseReproduct
	 *            the phaseReproduct to set
	 */
	public void setPhaseReproduct(double phaseReproduct) {
		this.phaseReproduct = phaseReproduct;
	}

	/**
	 * @return the initialType
	 */
	public int getInitialType() {
		return initialType;
	}

	/**
	 * @param initialType
	 *            the initialType to set
	 */
	public void setInitialType(int initialType) {
		this.initialType = initialType;
	}

	/**
	 * @return the global
	 */
	public Global getGlobal() {
		return global;
	}

	/**
	 * @param global
	 *            the global to set
	 */
	public void setGlobal(Global global) {
		this.global = global;
	}

	/**
	 * @return the dir
	 */
	public double getDir() {
		return dir;
	}

	/**
	 * @param dir
	 *            the dir to set
	 */
	public void setDir(double dir) {
		this.dir = dir;
	}

	/**
	 * @return the theta
	 */
	public double getTheta() {
		return theta;
	}

	/**
	 * @param theta
	 *            the theta to set
	 */
	public void setTheta(double theta) {
		this.theta = theta;
	}

	/**
	 * @return the xOld
	 */
	public double getXOld() {
		return xOld;
	}

	/**
	 * @param old
	 *            the xOld to set
	 */
	public void setXOld(double old) {
		xOld = old;
	}

	/**
	 * @return the yOld
	 */
	public double getYOld() {
		return yOld;
	}

	/**
	 * @param old
	 *            the yOld to set
	 */
	public void setYOld(double old) {
		yOld = old;
	}

	/**
	 * @return the dirOld
	 */
	public double getDirOld() {
		return dirOld;
	}

	/**
	 * @param dirOld
	 *            the dirOld to set
	 */
	public void setDirOld(double dirOld) {
		this.dirOld = dirOld;
	}

	/**
	 * @return the thetaOld
	 */
	public double getThetaOld() {
		return thetaOld;
	}

	/**
	 * @param thetaOld
	 *            the thetaOld to set
	 */
	public void setThetaOld(double thetaOld) {
		this.thetaOld = thetaOld;
	}

	/**
	 * @return the phaseOld
	 */
	public double getPhaseOld() {
		return phaseOld;
	}

	/**
	 * @param phaseOld
	 *            the phaseOld to set
	 */
	public void setPhaseOld(double phaseOld) {
		this.phaseOld = phaseOld;
	}

	/**
	 * @param iD
	 *            the iD to set
	 */
	public void setID(int iD) {
		ID = iD;
	}

	/**
	 * @return the iD
	 */
	public int getID() {
		return ID;
	}

	public double getVPhaseMean() {
		return vPhaseMean;
	}

	public void setVPhaseMean(double phaseMean) {
		vPhaseMean = phaseMean;
	}

	public double getVPhaseStd() {
		return vPhaseStd;
	}

	public void setVPhaseStd(double phaseStd) {
		vPhaseStd = phaseStd;
	}

	public double getVMoterMean() {
		return vMoterMean;
	}

	public void setVMoterMean(double moterMean) {
		vMoterMean = moterMean;
	}

	public double getVMoterStd() {
		return vMoterStd;
	}

	public void setVMoterStd(double moterStd) {
		vMoterStd = moterStd;
	}

	/**
	 * @return the ifReverse
	 */
	public boolean isIfReverse() {
		return ifReverse;
	}

	/**
	 * @param ifReverse
	 *            the ifReverse to set
	 */
	public void setIfReverse(boolean ifReverse) {
		this.ifReverse = ifReverse;
	}

	/**
	 * @return the numReverse
	 */
	public int getNumReverse() {
		return numReverse;
	}

	/**
	 * @param numReverse
	 *            the numReverse to set
	 */
	public void setNumReverse(int numReverse) {
		this.numReverse = numReverse;
	}

	/**
	 * @return the reverseTimer
	 */
	public double getReverseTimer() {
		return reverseTimer;
	}

	/**
	 * @param reverseTimer
	 *            the reverseTimer to set
	 */
	public void setReverseTimer(double reverseTimer) {
		this.reverseTimer = reverseTimer;
	}

	/**
	 * @return the initialPositionX
	 */
	public double getInitialPositionX() {
		return initialPositionX;
	}

	/**
	 * @param initialPositionX
	 *            the initialPositionX to set
	 */
	public void setInitialPositionX(double initialPositionX) {
		this.initialPositionX = initialPositionX;
	}

	/**
	 * @return the initialPositionY
	 */
	public double getInitialPositionY() {
		return initialPositionY;
	}

	/**
	 * @param initialPositionY
	 *            the initialPositionY to set
	 */
	public void setInitialPositionY(double initialPositionY) {
		this.initialPositionY = initialPositionY;
	}

	/**
	 * @return the meanPositionX
	 */
	public double getMeanPositionX() {
		return meanPositionX;
	}

	/**
	 * @param meanPositionX
	 *            the meanPositionX to set
	 */
	public void setMeanPositionX(double meanPositionX) {
		this.meanPositionX = meanPositionX;
	}

	/**
	 * @return the meanPositionY
	 */
	public double getMeanPositionY() {
		return meanPositionY;
	}

	/**
	 * @param meanPositionY
	 *            the meanPositionY to set
	 */
	public void setMeanPositionY(double meanPositionY) {
		this.meanPositionY = meanPositionY;
	}

	/**
	 * @return the revStartX
	 */
	public double getRevStartX() {
		return revStartX;
	}

	/**
	 * @param revStartX
	 *            the revStartX to set
	 */
	public void setRevStartX(double revStartX) {
		this.revStartX = revStartX;
	}

	/**
	 * @return the revStartY
	 */
	public double getRevStartY() {
		return revStartY;
	}

	/**
	 * @param revStartY
	 *            the revStartY to set
	 */
	public void setRevStartY(double revStartY) {
		this.revStartY = revStartY;
	}

	/**
	 * @return the xDisFromIni
	 */
	public double getXDisFromIni() {
		return xDisFromIni;
	}

	/**
	 * @param disFromIni
	 *            the xDisFromIni to set
	 */
	public void setXDisFromIni(double disFromIni) {
		xDisFromIni = disFromIni;
	}

	/**
	 * @return the yDisFromIni
	 */
	public double getYDisFromIni() {
		return yDisFromIni;
	}

	/**
	 * @param disFromIni
	 *            the yDisFromIni to set
	 */
	public void setYDisFromIni(double disFromIni) {
		yDisFromIni = disFromIni;
	}

	/**
	 * @return the xDisFromMean
	 */
	public double getXDisFromMean() {
		return xDisFromMean;
	}

	/**
	 * @param disFromMean
	 *            the xDisFromMean to set
	 */
	public void setXDisFromMean(double disFromMean) {
		xDisFromMean = disFromMean;
	}

	/**
	 * @return the yDisFromMean
	 */
	public double getYDisFromMean() {
		return yDisFromMean;
	}

	/**
	 * @param disFromMean
	 *            the yDisFromMean to set
	 */
	public void setYDisFromMean(double disFromMean) {
		yDisFromMean = disFromMean;
	}

	/**
	 * @return the xDisFromRevStart
	 */
	public double getXDisFromRevStart() {
		return xDisFromRevStart;
	}

	/**
	 * @param disFromRevStart
	 *            the xDisFromRevStart to set
	 */
	public void setXDisFromRevStart(double disFromRevStart) {
		xDisFromRevStart = disFromRevStart;
	}

	/**
	 * @return the yDisFromRevStart
	 */
	public double getYDisFromRevStart() {
		return yDisFromRevStart;
	}

	/**
	 * @param disFromRevStart
	 *            the yDisFromRevStart to set
	 */
	public void setYDisFromRevStart(double disFromRevStart) {
		yDisFromRevStart = disFromRevStart;
	}

	/**
	 * @return the trackLabel
	 */
	public boolean isTrackLabel() {
		return trackLabel;
	}

	/**
	 * @param trackLabel
	 *            the trackLabel to set
	 */
	public void setTrackLabel(boolean trackLabel) {
		this.trackLabel = trackLabel;
	}

	/**
	 * @param generalTrack
	 *            the generalTrack to set
	 */
	public void setGeneralTrack(boolean generalTrack) {
		this.generalTrack = generalTrack;
	}

	/**
	 * @return the generalTrack
	 */
	public boolean isGeneralTrack() {
		return generalTrack;
	}

	/**
	 * @param generalIniX
	 *            the generalIniX to set
	 */
	public void setGeneralIniX(double generalIniX) {
		this.generalIniX = generalIniX;
	}

	/**
	 * @return the generalIniX
	 */
	public double getGeneralIniX() {
		return generalIniX;
	}

	/**
	 * @param generalIniY
	 *            the generalIniY to set
	 */
	public void setGeneralIniY(double generalIniY) {
		this.generalIniY = generalIniY;
	}

	/**
	 * @return the generalIniY
	 */
	public double getGeneralIniY() {
		return generalIniY;
	}

	/**
	 * @param generalMeanX
	 *            the generalMeanX to set
	 */
	public void setGeneralMeanX(double generalMeanX) {
		this.generalMeanX = generalMeanX;
	}

	/**
	 * @return the generalMeanX
	 */
	public double getGeneralMeanX() {
		return generalMeanX;
	}

	/**
	 * @param generalMeanY
	 *            the generalMeanY to set
	 */
	public void setGeneralMeanY(double generalMeanY) {
		this.generalMeanY = generalMeanY;
	}

	/**
	 * @return the generalMeanY
	 */
	public double getGeneralMeanY() {
		return generalMeanY;
	}

	/**
	 * @param generalXDisFromIni
	 *            the generalXDisFromIni to set
	 */
	public void setGeneralXDisFromIni(double generalXDisFromIni) {
		this.generalXDisFromIni = generalXDisFromIni;
	}

	/**
	 * @return the generalXDisFromIni
	 */
	public double getGeneralXDisFromIni() {
		return generalXDisFromIni;
	}

	/**
	 * @param generalYDisFromIni
	 *            the generalYDisFromIni to set
	 */
	public void setGeneralYDisFromIni(double generalYDisFromIni) {
		this.generalYDisFromIni = generalYDisFromIni;
	}

	/**
	 * @return the generalYDisFromIni
	 */
	public double getGeneralYDisFromIni() {
		return generalYDisFromIni;
	}

	/**
	 * @param generalXDisFromMean
	 *            the generalXDisFromMean to set
	 */
	public void setGeneralXDisFromMean(double generalXDisFromMean) {
		this.generalXDisFromMean = generalXDisFromMean;
	}

	/**
	 * @return the generalXDisFromMean
	 */
	public double getGeneralXDisFromMean() {
		return generalXDisFromMean;
	}

	/**
	 * @param generalYDisFromMean
	 *            the generalYDisFromMean to set
	 */
	public void setGeneralYDisFromMean(double generalYDisFromMean) {
		this.generalYDisFromMean = generalYDisFromMean;
	}

	/**
	 * @return the generalYDisFromMean
	 */
	public double getGeneralYDisFromMean() {
		return generalYDisFromMean;
	}

	/**
	 * @return the inFbTimer
	 */
	public double getInFbTimer() {
		return inFbTimer;
	}

	/**
	 * @param inFbTimer
	 *            the inFbTimer to set
	 */
	public void setInFbTimer(double inFbTimer) {
		this.inFbTimer = inFbTimer;
	}

	/**
	 * @return the outFbTimer
	 */
	public double getOutFbTimer() {
		return outFbTimer;
	}

	/**
	 * @param outFbTimer
	 *            the outFbTimer to set
	 */
	public void setOutFbTimer(double outFbTimer) {
		this.outFbTimer = outFbTimer;
	}

	/**
	 * @param stats
	 *            the stats to set
	 */
	public void setStats(Statistics stats) {
		this.stats = stats;
	}

	/**
	 * @return the stats
	 */
	public Statistics getStats() {
		return stats;
	}

}

